package com.likan.chqjtgc.config;

import com.likan.chqjtgc.commons.AuthLoginClientConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * 授权服务器配置
 *
 * @author：liuxinghong <p>
 * 参考于   码云：https://gitee.com/hengboy
 * ================================
 * </p>
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    /**
     * 注入认证管理
     */
    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;
    /**
     * 密码加密方式
     */
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    @Qualifier("userDetailServiceImpl")
    private UserDetailsService userDetailsService;
    /**
     * token过期时间
     */
    @Value("${token-expiry-time}")
    private Integer tokenExpiryTime;

    /**
     * redis连接工厂类
     */
    @Autowired
    public RedisConnectionFactory redisConnectionFactory;

    /**
     * 开启授权服务器的access_token以及check_token节点
     * access_token路径开放使用
     * check_token路径需要认证后使用
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security)  {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()");
    }

    /**
     * 客户端相关配置
     * 未加密的secret为：clientSecret
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
       //内存模式
        clients
                .inMemory()
                //获取token登录客户端1  客户端id  密码
                .withClient(AuthLoginClientConfig.WEB_APP_CLIENT_ID)
                .authorizedGrantTypes("password", "authorization_code", "refresh_token")
                .scopes("all")
                .secret(passwordEncoder.encode(AuthLoginClientConfig.SECRET))
                //该客户端登录下需要对哪写模块做权限校验
                .resourceIds(AuthLoginClientConfig.mobile,AuthLoginClientConfig.basic,AuthLoginClientConfig.search,AuthLoginClientConfig.messageQueue)
                //token过期时间秒
                .accessTokenValiditySeconds(tokenExpiryTime)
                //刷新token过期时间秒
                .refreshTokenValiditySeconds(tokenExpiryTime)

                .and()
                //获取token登录客户端2 客户端id  密码
                .withClient(AuthLoginClientConfig.MOBILE_CLIENT_ID)
                .secret(passwordEncoder.encode(AuthLoginClientConfig.SECRET))
                .scopes("all")
                //token过期时间秒
                .accessTokenValiditySeconds(tokenExpiryTime)
                //刷新token过期时间秒
                .refreshTokenValiditySeconds(tokenExpiryTime)
                //该客户端登录下需要对哪写模块做权限校验
                .resourceIds(AuthLoginClientConfig.mobile,AuthLoginClientConfig.basic,AuthLoginClientConfig.search,AuthLoginClientConfig.messageQueue)
                .authorizedGrantTypes("password", "authorization_code", "refresh_token")
                .autoApprove(true);

        //配置客户端存储到db 代替原来得内存模式  第二种配置模式
//        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
//        clientDetailsService.setPasswordEncoder(passwordEncoder);
//        clients.withClientDetails(clientDetailsService);
    }




    /**
     * 配置授权节点信息
     *
     * @param endpoints 切点
     * @throws Exception 异常
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                //用户权限获得
                //若无，refresh_token会有UserDetailsService is required错误
                .userDetailsService(userDetailsService)
                // 配置token存储源
                .tokenStore(tokenStore())
                // 配置权限管理
                .authenticationManager(authenticationManager)
                // 配置access_token使用的转换
                .accessTokenConverter(accessTokenConverter());
        //token具体设置
//                .tokenServices(tokenService());
        // 自定义token生成方式
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(customerEnhancer(), accessTokenConverter()));
        endpoints.tokenEnhancer(tokenEnhancerChain);
        // 配置TokenServices参数
        DefaultTokenServices tokenServices = (DefaultTokenServices) endpoints.getDefaultAuthorizationServerTokenServices();
        tokenServices.setTokenStore(endpoints.getTokenStore());
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
        tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
        //设置登录端有效期 30天(int)
        tokenServices.setAccessTokenValiditySeconds(Long.valueOf(TimeUnit.DAYS.toSeconds(30)).intValue());
        endpoints.tokenServices(tokenServices);
        super.configure(endpoints);
    }


    /**
     * 配置token存储源
     * 采用redis作为token的存储
     * 并且使用自定义的 加密Key进行加密
     */
    @Bean
    public TokenStore tokenStore() {
        //使用redis存储token
        RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
        //设置redis token存储中的前缀
        redisTokenStore.setPrefix(AuthLoginClientConfig.JWT_REDIS_PREFIX);
        return redisTokenStore;
    }


    //    /**
//     * 配置token存储源
//     * 采用jwt作为Token生成格式
//     * 并且使用自定义的 加密Key进行加密
//     *
//     * @return
//     */
//    @Bean
//    public TokenStore tokenStore() {
//        return new JwtTokenStore(jwtAccessTokenConverter());
//    }

        /**
     * 配置jwt生成token的转换
     * 使用自定义Sign Key 进行加密
     */
        @Bean
        public JwtAccessTokenConverter accessTokenConverter(){
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            converter.setSigningKey(AuthLoginClientConfig.JWT_SECRET_KEY);
            return converter;
        }

    /**
     * 注入自定义token生成方式
     */
    @Bean
    public TokenEnhancer customerEnhancer() {
        return new CustomTokenEnhancer();
    }

//
//    @Bean
//    public DefaultTokenServices tokenService() {
//        DefaultTokenServices tokenServices = new DefaultTokenServices();
//        //配置token存储
//        tokenServices.setTokenStore(tokenStore());
//        //开启支持refresh_token，此处如果之前没有配置，启动服务后再配置重启服务，可能会导致不返回token的问题，解决方式：清除redis对应token存储
//        tokenServices.setSupportRefreshToken(true);
//        //复用refresh_token
//        tokenServices.setReuseRefreshToken(true);
//        //token有效期，设置12小时
//        tokenServices.setAccessTokenValiditySeconds(12 * 60 * 60);
//        //refresh_token有效期，设置一周
//        tokenServices.setRefreshTokenValiditySeconds(7 * 24 * 60 * 60);
//        tokenServices.setAuthenticationManager(authenticationManager);
//        return tokenServices;
//    }
}
